package dtm

import (
	"context"
	"database/sql"
	"fmt"
	"time"

	"mall/common/globalkey"
	"mall/common/resultx"
	"mall/service/backend/common/i18n"
	"mall/service/order/model"
	"mall/service/order/rpc/internal/svc"
	"mall/service/order/rpc/types/order"

	"github.com/dtm-labs/client/dtmcli"
	"github.com/dtm-labs/client/dtmgrpc"
	"github.com/sirupsen/logrus"
	"github.com/volatiletech/null/v8"
	"github.com/volatiletech/sqlboiler/v4/boil"
	"github.com/zeromicro/go-zero/core/logx"
	"google.golang.org/grpc/codes"
)

type UpdateTradeStateRawLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewUpdateTradeStateRawLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateTradeStateRawLogic {
	return &UpdateTradeStateRawLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *UpdateTradeStateRawLogic) UpdateTradeStateRaw(in *order.UpdateTradeStateReq) (*order.BaseResp, error) {
	logrus.Info(fmt.Sprintf("UpdateTradeStateRaw UpdateTradeStateReq: %+v", in))

	if in.Id == nil || *in.Id == 0 {
		return nil, resultx.StatusError(resultx.ORDER_ID_REQUIRED, "")
	}

	if in.TradeState == nil || *in.TradeState == 0 {
		return nil, resultx.StatusError(resultx.ORDER_TRADE_STATE_INVALID, "")
	}

	// 获取子事务屏障对象
	barrier, err := dtmgrpc.BarrierFromGrpc(l.ctx)
	if err != nil {
		// 重试
		return nil, resultx.StatusError(resultx.DTM_ERROR, err.Error())
	}

	// 开启子事务屏障
	err = barrier.CallWithDB(l.svcCtx.DB, func(tx *sql.Tx) error {
		trade, err := l.svcCtx.Order.FindOneNoCache(l.ctx, tx, *in.Id)
		if err != nil {
			if err == sql.ErrNoRows {
				return resultx.StatusError(resultx.ORDER_ID_INVALID, "")
			}

			return err
		}

		now := time.Now()
		paidAt := null.TimeFrom(now)
		var status uint32 = *in.TradeState

		if in.PaymentId != nil && *in.PaymentId > 0 {
			payment, err := model.FindOrderPayment(l.ctx, tx, *in.PaymentId)
			if err != nil {
				if err == sql.ErrNoRows {
					return resultx.StatusError(resultx.ORDER_PAYMENT_ID_INVALID, "")
				}

				return err
			}

			if status == globalkey.OrderTradeStatePaid {
				payment.PaidAt = paidAt
			} else if status == globalkey.OrderTradeStateNotPay {
				payment.PaidAt = null.TimeFromPtr(nil)
			}
			payment.Status = uint8(status)
			if in.TransactionId != nil && len(*in.TransactionId) > 0 {
				payment.TransactionID = *in.TransactionId
			}
			rowsAff, err := payment.Update(l.ctx, tx, boil.Whitelist(
				model.OrderPaymentColumns.TransactionID,
				model.OrderPaymentColumns.Status,
				model.OrderPaymentColumns.PaidAt,
			))
			if err != nil {
				return err
			}

			if rowsAff == 0 {
				return dtmcli.ErrFailure
			}
		}

		orderType := uint32(trade.OrderType)
		if status == globalkey.OrderTradeStatePaid {
			trade.PaidAt = paidAt
			if orderType == globalkey.OrderTypeVirtual {
				status = globalkey.OrderTradeStateFulfilled
				trade.FulfilledAt = paidAt
			}
		} else if status == globalkey.OrderTradeStateNotPay {
			trade.PaidAt = null.TimeFromPtr(nil)
		}
		trade.TradeState = uint16(status)
		err = l.svcCtx.Order.Update(l.ctx, tx, trade, nil)
		if err != nil {
			return err
		}

		return nil
	})

	// 不再重试，走回滚
	if err == dtmcli.ErrFailure {
		// 回滚状态
		return nil, resultx.StatusError(uint32(codes.Aborted), dtmcli.ResultFailure)
	}

	if err != nil {
		return nil, err
	}

	return &order.BaseResp{
		Msg: i18n.UpdateSuccess,
	}, nil
}
